<template>
  <!-- 滚动容器，必须有固定高度 -->
  <div class="scroll-container" ref="scrollRef">
    <div class="scroll-content">
      <!-- 下拉刷新的提示区域，通过绝对定位实现 -->
      <div class="pull-down" :class="{ pulling: isPullingDown }">
        <van-loading v-if="isPullingDown" size="16" class="loading">加载中...</van-loading>
        <span v-else class="text">{{ pullDownText }}</span>
      </div>

      <!-- 列表内容插槽，传入list数据供外部使用 -->
      <slot :list="list"></slot>

      <!-- 上拉加载的提示区域，在列表底部 -->
      <div class="pull-up">
        <template v-if="!finished">
          <van-loading v-if="loading" size="16" class="loading">加载中...</van-loading>
          <span v-else class="text">{{ pullUpText }}</span>
        </template>
        <span v-else class="text">没有更多了</span>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
// 引入 BetterScroll 核心和插件
import BScroll from '@better-scroll/core'
import PullDown from '@better-scroll/pull-down'
import PullUp from '@better-scroll/pull-up'
import { showToast } from 'vant'

// 注册下拉刷新和上拉加载插件
BScroll.use(PullDown)
BScroll.use(PullUp)

// 组件属性定义
interface Props {
  requestApi: Function // 请求数据的API方法
  requestParams?: Record<string, any> // 额外的请求参数
  immediate?: boolean // 是否立即加载第一页数据
}

const props = withDefaults(defineProps<Props>(), {
  requestParams: () => ({}),
  immediate: true
})

// ===== 状态管理 =====
const list = ref<any[]>([]) // 列表数据
const pageNum = ref(1) // 当前页码
const scrollRef = ref<HTMLElement>() // 滚动容器引用
const bs = ref<any>(null) // BetterScroll 实例
const loading = ref(false) // 是否正在加载
const finished = ref(false) // 是否加载完成
const isPullingDown = ref(false) // 是否正在下拉刷新
const pullDownText = ref('下拉刷新') // 下拉提示文本
const pullUpText = ref('上拉加载更多') // 上拉提示文本

// ===== 核心方法：数据加载 =====
const loadData = async (isPullDown = false) => {
  try {
    // 设置加载状态
    if (isPullDown) {
      pageNum.value = 1 // 刷新时重置页码
      isPullingDown.value = true
    } else {
      loading.value = true
    }

    // 请求数据
    const res = await props.requestApi({
      ...props.requestParams,
      page: pageNum.value,
      pageSize: 10
    })

    const { list: newList, pagination } = res.data

    // 处理数据：刷新替换，加载追加
    if (isPullDown) {
      list.value = newList
    } else {
      list.value.push(...newList)
    }

    // 更新分页状态
    if (pagination.hasMore && newList.length > 0) {
      pageNum.value++ // 页码加1
      finished.value = false
    } else {
      finished.value = true // 标记加载完成
    }

    return pagination.hasMore
  } catch (err) {
    console.error('加载失败:', err)
    showToast('加载失败')
    return false
  } finally {
    // 重置加载状态
    if (isPullDown) {
      isPullingDown.value = false
    } else {
      loading.value = false
    }
  }
}

// ===== 初始化滚动组件 =====
onMounted(async () => {
  if (!scrollRef.value) return

  // 创建 BetterScroll 实例
  bs.value = new BScroll(scrollRef.value, {
    click: true, // 允许点击事件
    probeType: 3, // 实时派发滚动事件
    bounce: {
      top: true, // 允许顶部回弹
      bottom: true // 允许底部回弹
    },
    pullDownRefresh: {
      threshold: 70, // 下拉距离阈值
      stop: 50 // 回弹停留位置
    },
    pullUpLoad: {
      threshold: 50, // 上拉距离阈值
      stop: 20 // 回弹停留位置
    }
  })

  // 监听下拉刷新事件
  bs.value.on('pullingDown', async () => {
    pullDownText.value = '释放刷新'
    await loadData(true)
    await bs.value.finishPullDown() // 结束下拉刷新
    bs.value.refresh() // 刷新滚动高度
    pullDownText.value = '下拉刷新'
  })

  // 监听上拉加载事件
  bs.value.on('pullingUp', async () => {
    if (loading.value || finished.value) return
    const hasMore = await loadData(false)
    await bs.value.finishPullUp() // 结束上拉加载
    if (!hasMore) {
      bs.value.closePullUp() // 关闭上拉加载
    } else {
      bs.value.refresh() // 刷新滚动高度
    }
  })

  // 初始化时加载第一页数据
  if (props.immediate) {
    await loadData(true)
    bs.value.refresh()
  }
})

// 组件销毁时清理资源
onBeforeUnmount(() => {
  bs.value?.destroy()
})

// 暴露刷新方法供外部调用
defineExpose({
  refresh: async () => {
    finished.value = false
    await loadData(true)
    bs.value?.refresh()
  }
})
</script>

<style lang="less" scoped>
// 样式说明：
// 1. scroll-container 必须有固定高度
// 2. scroll-content 设置最小高度确保可滚动
// 3. pull-down 通过绝对定位实现下拉动画
// 4. pull-up 在列表底部显示加载状态
.scroll-container {
  height: 100%;
  overflow: hidden;
  position: relative;

  .scroll-content {
    min-height: 101%;
  }

  .pull-down,
  .pull-up {
    height: 50px;
    line-height: 50px;
    text-align: center;
    color: #969799;
    font-size: 14px;

    .loading {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      color: #969799;
    }
  }

  .pull-down {
    position: absolute;
    top: -50px;
    left: 0;
    width: 100%;
    transition: all 0.3s;

    &.pulling {
      transform: translateY(50px);
    }
  }

  .pull-up {
    width: 100%;
  }
}
</style>
